<template>
  <view class="xl-cascade" :style="{'--height': $xl.calcUnit(height, 'rpx'), '--left-width': $xl.calcUnit(leftWidth, 'rpx'), '--bg-color': background}">
    <view v-if="!hiddenLeft" class="xl-cascade-left">
      <scroll-view class="left-scroll" scroll-y scroll-with-animation>
        <view class="left-category">
          <view v-for="(category, index) in leftData" :key="index" class="category-item"
                :class="cur === index && 'active'" @click="clickLeftItem(category, index)">
            <text class="category-item-name">{{ category[propNameConfig.name] }}</text>
            <view class="category-item-line"></view>
          </view>
        </view>
      </scroll-view>
    </view>
    <view class="xl-cascade-right" :class="{ first: cur === 0 }" :style="{'--capsule-bg': capsuleBg, '--banner-height': bannerUrl ? '190rpx' : '0%'}">
      <view v-if="bannerUrl && showBanner" class="banner-box">
        <xl-image class="banner-img" :src="bannerUrl" mode="aspectFix" width="100%" height="160rpx" :borderRadius="12"></xl-image>
      </view>
      <view v-if="loading" class="loading-box">
        <xl-loading type="swappingSquares" :color="color"></xl-loading>
        <text class="loading-text">加载中...</text>
      </view>
      <view v-else-if="!rightData.length" class="empty-box">
        <xl-empty type="data" text="暂无分类"></xl-empty>
      </view>
      <view v-else class="right-category-box">
        <scroll-view class="right-category-scroll" scroll-y scroll-with-animation :scroll-into-view="toIndex">
          <view class="right-category">
            <view v-for="(category, index) in rightData" :key="index" :id="'right-category-item-'+index" class="right-category-item">
              <view class="right-category-item-title" :data-index="index">{{ category[propNameConfig.name] }}</view>
              <view v-if="type==='img'" class="right-category-item-list">
                <view v-if="showAll" class="right-category-item-list-item" @click="clickItem(category)">
                  <view class="category-item-image">
                    <xl-image
                        :src="category[propNameConfig.img]"
                        width="120"
                        height="120"
                        :borderRadius="4"
                    ></xl-image>
                  </view>
                  <text class="category-item-name">全部</text>
                </view>
                <view v-for="item in category[propNameConfig.children]" :key="item[propNameConfig.id]"
                      class="right-category-item-list-item"
                      @click="clickItem(item)"
                >
                  <view class="category-item-image">
                    <xl-image
                        :src="item[propNameConfig.img]"
                        width="120"
                        height="120"
                        :borderRadius="4"
                    ></xl-image>
                  </view>
                  <text class="category-item-name">{{ item[propNameConfig.name] }}</text>
                </view>
              </view>
              <view v-else-if="type==='capsule'" class="right-category-item-capsule">
                <view v-if="showAll" :class="['right-category-item-capsule-item', category[propNameConfig.id] === curId && 'capsule-item-checked']" @click="clickItem(category)">
                  <view class="item-text">全部</view>
                </view>
                <view v-for="(item, index) in category[propNameConfig.children]" :key="item[propNameConfig.id]"
                      :class="['right-category-item-capsule-item', item[propNameConfig.id] === curId && 'capsule-item-checked', item.disable && 'capsule-item-disable']" @click="clickItem(item)">
                  <view class="item-text">{{item[propNameConfig.name]}}</view>
                </view>
              </view>
            </view>
          </view>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, watch, onMounted, onBeforeUnmount, getCurrentInstance, nextTick } from 'vue'
import propsData from './props.js'
const props = defineProps(propsData)
const emits = defineEmits(['load', 'loadChildren', 'click'])

const cur = ref(0)
// 属性配置
const propNameConfig = ref({id: 'id',name: 'name',children: 'children',img: 'img'})
// 左边数据
const leftData = ref([])
// 右边数据
const rightData = ref([])
// 右边顶部图片，二级分类时，组件传入，三级分类时，是一级分类的图片
const bannerUrl = ref('')
// 加载loading
const loading = ref(true)
// 胶囊背景色
const capsuleBg = ref("#f5f5f5");

// 二级分类时，用于左边点击，右边滚动至当前项
const toIndex = ref('right-category-item-0')
// 创建一个交叉观察器，监控scroll-view是否完全显示
let observer = null;
const instance = getCurrentInstance();
// 点击滚动时不触发
let clickScroll = false;

// 加载二级级联数据
function loadSecondary(){
  loading.value = true
  if(props.list){
    watch(() => props.list, newVal => {
      initSecondary(newVal)
    }, { immediate: true })
  }else if(props.loadApi){
    uni.$xl.apis[props.loadApi]().then(res => {
      initSecondary(res.data)
    }).catch(err => {
      uni.$xl.toast(err, 2000)
      loading.value = false
      leftData.value = []
      rightData.value = []
    })
  }else {
    emits('load', initSecondary)
  }
}
// 初始化二级级联数据
function initSecondary(list){
  // 之前有监听，停止监听
  if(observer) observer.disconnect();
  leftData.value = list
  rightData.value = list
  bannerUrl.value = props.banner
  loading.value = false
  if(!props.hiddenLeft){
    nextTick(()=>{
      observer = uni.createIntersectionObserver(instance, {
        thresholds: [0.7],			// 观察目标与视口相交部分，占观察目标的百分比
        observeAll: true, 			// 多个目标元素需要监听
        nativeMode: true			// 启用小程序原生的 IntersectionObserver。
      });
      const topHeight = 30 - uni.$xl.upx2px(parseInt(props.height) - (bannerUrl.value ? 190 : 0))
      // 标题进入scroll-view顶部30px高区域70%时，触发
      observer.relativeTo('.right-category-scroll', {bottom: topHeight}).observe('.right-category-item-title', (res) => {
        if(!clickScroll && res.intersectionRatio > 0.7){
          cur.value = res.dataset.index
          // 滚动切换后，toIndex要清空，否则再点击上次点击的，不会跳转
          toIndex.value = ''
        }
      })
    })
  }
}

// 加载三级级联数据
function loadTertiary(){
  loading.value = true
  if(props.list){
    watch(() => props.list, newVal => {
      initTertiary(newVal)
    }, { immediate: true })
  }else if(props.loadApi){
    uni.$xl.apis[props.loadApi]().then(res => {
      initTertiary(res.data)
    }).catch(err => {
      uni.$xl.toast(err, 2000)
      loading.value = false
      leftData.value = []
      rightData.value = []
    })
  }else {
    emits('load', initTertiary)
  }
}
// 初始化三级级联数据
function initTertiary(list){
  leftData.value = list
  if(leftData.value && leftData.value.length)
    loadChildren(leftData.value[cur.value])
}
// 加载下级数据，三级级联数据时有
function loadChildren(category){
  bannerUrl.value = category[propNameConfig.value.img]
  // 加载下级数据
  if(category[propNameConfig.value.children] && category[propNameConfig.value.children].length > 0){
    rightData.value = category[propNameConfig.value.children]
    loading.value = false
  }else if(props.loadChildApi){
    uni.$xl.apis[props.loadChildApi]({
      jointUrl: '/' + category[propNameConfig.value.id]
    }).then(res => {
      initChildren(category, res.data)
    }).catch(err => {
      rightLoading.value = false
      uni.$xl.toast(err, 2000)
      rightData.value = []
    })
  }else {
    emits('loadChildren', category, data => initChildren(category, data))
  }
}
function initChildren(category, children){
  category[propNameConfig.value.children] = children
  rightData.value = children
  loading.value = false
}

onMounted(() => {
  propNameConfig.value = {
    id: props.propNames?.id || 'id',
    name: props.propNames?.name || 'name',
    children: props.propNames?.children || 'children',
    img: props.propNames?.img || 'img',
    url: props.propNames?.url || 'url'
  }
  capsuleBg.value = uni.$xl.color.colorToRgba(props.color, 0.08);
  // 检查API是否存在
  if (process.env.NODE_ENV === 'development') {
    if(props.loadApi && !uni.$xl.apis[props.loadApi]) {
      console.error('您配置的loadApi接口方法不存在')
      return
    }
    if(props.loadChildApi && !uni.$xl.apis[props.loadChildApi]) {
      console.error('您配置的loadChildApi接口方法不存在')
      return
    }
  }
  if (props.cascade === 2) {
    loadSecondary()
  }else if (props.cascade === 3) {
    loadTertiary()
  }
})
onBeforeUnmount(() => {
  if (observer) observer.disconnect();
});

function clickLeftItem(category, index) {
  if (props.cascade === 2) {
    clickScroll = true
    cur.value = index
    toIndex.value = 'right-category-item-'+index
    // 为了防止点击后滚动更新 cur，延迟一段时间重新开启滚动监听
    setTimeout(() => clickScroll = false, 300);
  }else if(props.cascade === 3){
    cur.value = index
    loadChildren(category)
  }
}

function clickItem(category){
  if(category.disable) return
  if(props.url) {
    uni.$xl.router.push({ name: props.url, params: { id: category[propNameConfig.value.id] } })
  }else if(category[propNameConfig.value.url]){
    uni.$xl.router.push({ name: category[propNameConfig.value.url], params: { id: category[propNameConfig.value.id] } })
  }
  emits('click', category)
}
</script>


<style lang="scss" scoped>
.xl-cascade {
  display: flex;
  flex-direction: row;
  height: var(--height);

  .xl-cascade-left {
    width: var(--left-width);
    overflow: hidden;

    .left-scroll {
      width: calc(var(--left-width) + 20rpx);
      height: var(--height);

      .left-category {
        overflow: hidden;

        .category-item {
          position: relative;
          height: 100rpx;
          width: var(--left-width);
          display: flex;
          align-items: center;
          justify-content: center;

          &-name {
            font-size: 30rpx;
            color: #444;
            font-weight: 400;
          }

          &-line {
            position: absolute;
            left: 0;
            top: 27rpx;
            background: v-bind(color);
            width: 10rpx;
            height: 46rpx;
            display: none;
          }

          &.active {
            background-color: var(--bg-color);

            .category-item-name {
              font-size: 32rpx;
              font-weight: bold;
              color: v-bind(color);
            }

            .category-item-line {
              display: flex;
            }
          }

          --active-radius-size: 30rpx;

          &.active::before {
            content: '';
            position: absolute;
            z-index: 9;
            top: calc(-1 * var(--active-radius-size));
            right: 0;
            width: var(--active-radius-size);
            height: var(--active-radius-size);
            background-image: radial-gradient(circle at 0 0, transparent var(--active-radius-size), var(--bg-color) var(--active-radius-size));
          }

          &.active::after {
            content: '';
            position: absolute;
            z-index: 9;
            bottom: calc(-1 * var(--active-radius-size));
            right: 0;
            width: var(--active-radius-size);
            height: var(--active-radius-size);
            background-image: radial-gradient(circle at 0 100%, transparent var(--active-radius-size), var(--bg-color) var(--active-radius-size));
          }
        }
      }
    }
  }
  .xl-cascade-right{
    flex: 1;
    height: var(--height);
    background: v-bind(background);
    overflow: hidden;
    border-top-left-radius: 20rpx;
    border-top-right-radius: 20rpx;
    &.first{
      border-top-left-radius: 0;
    }
    .banner-box {
      box-sizing: border-box;
      padding: 15rpx 25rpx;
      margin: 0 auto;
      position: relative;
      .banner-img {
        border-radius: 12rpx;
      }
      .close {
        position: absolute;
        top: 6rpx;
        right: 6rpx;
        width: 26rpx;
        height: 26rpx;
        background-color: rgba(255,255,255,.4);
        border-radius: 100px;
      }
    }
    .loading-box {
      height: calc(var(--height) - var(--banner-height));
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      .loading-text {
        margin-top: 20px;
        font-size: 32rpx;
        color: v-bind(color);
      }
    }
    .empty-box {
      box-sizing: border-box;
      height: calc(var(--height) - var(--banner-height));
      padding-top: 80rpx;
    }
    .right-category-box {
      height: calc(var(--height) - var(--banner-height));
      overflow: hidden;
    }
    .right-category-scroll {
      width: calc(100% + 20rpx);
      height: calc(var(--height) - var(--banner-height));
      background: v-bind(background);
      .right-category {
        &-item {
          padding: 26rpx 0 16rpx;
          &-title {
            box-sizing: border-box;
            font-weight: 700;
            font-size: 32rpx;
            padding-left: 30rpx;
          }
          &-list {
            display: flex;
            flex-direction: row;
            flex-wrap: wrap;
            gap: 24rpx;
            padding: 24rpx;
            &-item {
              display: flex;
              flex-direction: column;
              justify-content: center;
              align-items: center;
            }
            .category-item-image {
              width: 140rpx;
              height: 140rpx;
              background-color: rgba(250,250,250,.7);
              border-radius: 10rpx;
              display: flex;
              align-items: center;
              justify-content: center;
            }
            .category-item-name {
              margin-top: 10rpx;
              font-size: 28rpx;
              color: #333;
            }
          }
          &-capsule{
            /* #ifndef APP-NVUE */
            display: flex;
            /* #endif */
            flex-direction: row;
            justify-content: flex-start;
            align-items: center;
            flex-wrap: wrap;
            gap: 20rpx;
            padding: 32rpx 24rpx 0rpx;
            &-item{
              padding: 10rpx 26rpx;
              border-radius: 100rpx;
              background: #f5f5f5;
              .item-text{
                font-size: 26rpx;
                color: #333333;
              }
              &.capsule-item-checked{
                background: var(--capsule-bg);
                .item-text{
                  color: v-bind(color);
                }
              }
              &.capsule-item-disable{
                background: #f5f5f5;
                .item-text{
                  color: #bfbfbf;
                }
              }
            }
          }
        }
      }
    }
  }
}
</style>